package model;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class CodeWords implements ICodeWords {
	
	// FIELDS
	private static final long serialVersionUID = 5765414920448214380L;
	
	// Maps a code to her specific decoded word
	private Map<String, String> code;
	
	// CONSTRUCTEURS
	public CodeWords(Map<String, String> code) {
		if (code == null) {
			throw new IllegalArgumentException("code argument is null");
		}
		this.code = code;
	}
	
	public CodeWords() {
		this(new HashMap<String, String>());
	}

	// REQUETES
	public boolean contains(String word) {
		if (word == null) {
			throw new IllegalArgumentException("word argument is null");
		}
		return this.code.keySet().contains(word);
	}
	
	public Set<String> getPrefixSet() {
		Set<String> temp = new HashSet<String>();
		int wordLength;
		for (String word: this.code.keySet()) {
			wordLength = word.length();
			for (int i = 1; i < wordLength; i++) {
				temp.add(word.substring(0, i));
			}
		}
		return temp;
	}

	public Set<String> getSuffixSet() {
		Set<String> temp = new HashSet<String>();
		int wordLength;
		for (String word: this.code.keySet()) {
			wordLength = word.length();
			for (int i = 1; i < wordLength; i++) {
				temp.add(word.substring(wordLength - i, wordLength));
			}
		}
		return temp;
	}
	
	public Set<String> getContentSet() {
		return this.code.keySet();
	}
	
	public String getTranslation(String word) {
		if (word == null) {
			throw new IllegalArgumentException("word argument is null");
		}
		if (this.code.containsKey(word)) {
			return this.code.get(word);
		}
		return null;
	}
	
	public Map<String, String> getTranslations(Set<String> words) {
		if (words == null) {
			throw new IllegalArgumentException("words argument is null");
		}
		Map<String, String> tempMap = new HashMap<String, String>();
		for (String w: words) {
			if (this.code.containsKey(w)) {
				tempMap.put(w, this.code.get(w));
			}
		}
		return tempMap;
	}
	
	public Map<String, String> getAllTranslations() {
		return this.code;
	}
	
	public boolean isUniquelyDecodable() {
		Set<String> temp1;
		Set<String> temp2;
		Set<String> r = getRightRemainder(this.code.keySet(), this.code.keySet());
		r.remove("");
		Set<Set<String>> computedRests = new HashSet<Set<String>>();
		boolean isAmbiguous = false;
		while(!r.isEmpty() && !isAmbiguous) {
			if (computedRests.contains(r)) {
				return true;
			} else {
				computedRests.add(r);
				isAmbiguous = !intersection(r, this.code.keySet()).isEmpty();
				if (!isAmbiguous) {
					temp1 = getRightRemainder(this.code.keySet(), r);
					temp2 = getRightRemainder(r, this.code.keySet());
					r = union(temp1, temp2);
				}
			}
		}
		return !isAmbiguous;
	}
	
	public boolean isEmpty() {
		return this.code.keySet().isEmpty();
	}
	
	public String toString() {
		String temp = "Codewords = {";
		int i = 1;
		int size = this.code.keySet().size();
		for (String v: this.code.keySet()) {
			temp += v;
			if (i < size) {
				temp += ",";
			}
			i++;
		}
		temp += "}";
		return temp;
	}

	// COMMANDES
	public void addWord(String word, String decodedWord) {
		if (word == null) {
			throw new IllegalArgumentException("word argument is null");
		}
		if (decodedWord == null) {
			throw new IllegalArgumentException("decoded word argument is null");
		}
		this.code.put(word, decodedWord);
	}
	
	public void removeWord(String word) {
		if (word == null) {
			throw new IllegalArgumentException("word argument is null");
		}
		this.code.remove(word);
	}

	public void addWords(Map<String, String> code) {
		if (code == null) {
			throw new IllegalArgumentException("code argument is null");
		}
		this.code.putAll(code);
	}
	
	public void removeWords(Set<String> words) {
		if (words == null) {
			throw new IllegalArgumentException("words argument is null");
		}
		for (String w: words) {
			removeWord(w);
		}
	}

	public void clear() {
		this.code.clear();
	}
	
	// TOOLS
	private Set<String> getRightRemainder(Set<String> leftTokens, Set<String> target) {
		if (leftTokens == null) {
			throw new IllegalArgumentException("leftTokens argument is null");
		}
		if (target == null) {
			throw new IllegalArgumentException("target argument is null");
		}
		Set<String> remainder = new HashSet<String>();
		for (String pref: leftTokens) {
			for (String tg: target) {
				if (tg.startsWith(pref)) {
					remainder.add(tg.substring(pref.length(), tg.length()));
				}
			}
		}
		return remainder;
	}
	
	private Set<String> union(Set<String> set1, Set<String> set2) {
		if (set1 == null) {
			throw new IllegalArgumentException("set1 argument is null");
		}
		if (set2 == null) {
			throw new IllegalArgumentException("set2 argument is null");
		}
		Set<String> union = new HashSet<String>();
		union.addAll(set1);
		union.addAll(set2);
		return union;
	}
	
	private Set<String> intersection(Set<String> set1, Set<String> set2) {
		if (set1 == null) {
			throw new IllegalArgumentException("set1 argument is null");
		}
		if (set2 == null) {
			throw new IllegalArgumentException("set2 argument is null");
		}
		Set<String> intersection = new HashSet<String>();
		intersection.addAll(set1);
		intersection.retainAll(set2);
		return intersection;
	}

}
